/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// drawing
#include "quakedef.h"

// optionlly disable predicates for light volume culling
// #define PREDICATES_SUCK_DIE_DIE_DIE

/*
- Don't do anything fancy when generating the shadow maps (variance shadow maps, geometry shader expansion, tessellation, etc.). Just render plain depth to a depth buffer (don't render to a color render target) with no pixel shader bound.

- Cull as aggressively as you can.
-- If a mesh isn't in the light's field of view, don't render it to the shadow map.
-- If a mesh doesn't cast a shadow on visible geometry (this is harder to check) then don't render it to the shadow map.
-- If a mesh is completely in shadow, then don't render it to the shadow map.
-- If a mesh casts a really small shadow relative to the screen, then consider not rendering it to the shadow map.
-- If a light doesn't intersect with the field of view, don't render it at all and skip the shadows.
-- If a light is completely occluded by geometry, skip it (can potentially use occlusion queries and predicated draws to do this)
-- If you're rendering 6 directions for a point light, skip any directions that aren't visible.
-- If no shadow-casting meshes intersect a light's field of view, turn off shadows for that light.
-- Consider not rendering shadows for lights that are far away.
-- If either your geometry or lights or static, precompute whatever you can.

- Consider casting shadows with simplified meshes. This isn't always something you can do since it can easily break self-shadowing, but it can work very well for certain cases.

- If meshes and lights are static, consider pre-culling backfacing triangles.

- If you're running on PC then number of draw calls can be an issue for CPU overhead, in which case it makes sense to use instancing if you can. You can also potentially pre-combine meshes to reduce draw counts. 


With DX11 (compute shaders + drawindirect + new tricks), you can render each shadow map with a single draw call. This will improve the performance a lot, since previously the high amount of draw calls were the most limiting factor of shadow mapping performance. In our Xbox 360 game, the shadow map rendering performed around 3x-4x more draw calls than the visible geometry pass. With new DX11 capable hardware, we just need a single draw (and a few compute shader dispatch calls) per shadow map. That's a huge gain. 


stuff i can do right now...

precompute world data to a static vertex buffer/index buffer combo for each static world light
cull agressively when doing this; since it's load-time precomputed we can do ray traces, frustum portals, occlusion queries, light PVS, whatever
just don't include it if it's not visible from the light

at load time, for each light:
- evaluate a PVS
- recururse through the world, evaluating surfaces and indexes
- add them to static buffers

at draw time, for each light
- if the light has a static world buffer, draw that
- if not, don't
- then do entities

since we already have all the world verts stored ina  buffer, we just need to store per-light indexes!!!

ztrick to avoid clears???  yes, we can do it with a colour buffer too, and at 32bit/limited range it should be OK
it's just a mathematical formula after all; switch the blend between min and max, load {0, 0.5} or {1, 0.5} to a per-light cbuffer,
then encode/decode from those values - should save LOTS of the fillrate burden
*/

/*
go for a multiple of 16 or 32 for the shadow map sizes, fix it at an upper bound, and put them into buckets

when a map is to be allocated, figure it's bucket (size / 32) and look for a free map in that bucket
if one is not found allocate a new one for real.

when a map is to be released, just return it to the appropriate bucket.  if there's no room in the
bucket we can release it for real instead

bug

if an entity wasn't in a previous PVS it's light won't be updated
*/

#define SHADOWMAP_BUCKET_SIZE	128
#define MAX_SHADOWMAP_SIZE		1024	// should be a multiple of SHADOWMAP_STEP_SIZE
#define SHADOWMAP_STEP_SIZE		32		// should be a power of two

QSHADER d3d_SurfLight;
QSHADER d3d_MeshLight;

QSHADER d3d_MeshShadowVolume;

QSHADER d3d_SurfShadow;
QSHADER d3d_MeshShadow;

QSHADER d3d_SurfShadowed;
QSHADER d3d_MeshShadowed;

QSHADER d3d_LightBounds;

QSHADER d3d_DeferredQuad;
QSHADER d3d_DeferredIcos;

QSHADER d3d_StencilClearQuad;
QSHADER d3d_StencilClearIcos;

QTEXTURE d3d_DynamicShadowMap;

// gl_ztrick adapted to shadow maps to avoid clearing the cubemap texture
// higher positive numbers are nearer to the light, use max blend, trickframe 1
// lower negative numbers are nearer to the light, use min blend, trickframe -1
// (somebignum - dist) * trickframe
// does this work with the same comparison in both directions???
static int d3d_ShadowTrickFrame = 0;

struct lightparameters_t
{
	// ideally these would be stored in this order in the dlight_t itself so we just update directly from that
	float lightposition[3];
	float lightradius;
	float lightcolour[3];
	float zpass;
	float falloff;
	float spotvec[3];
};


struct lightprojection_t
{
	// storing a full MVP for each cube side
	float matrix[6][16];
	float depthbias;
	float trickframe;
	float padding[2];
};


ID3D10Buffer *d3d_LightProjection = NULL;
ID3D10Buffer *d3d_LightParameters = NULL;
ID3D10Buffer *d3d_LightVertexes = NULL;

geo_t d3d_LightVolume;

struct lightvertex_t
{
	float Origin[3];
	float Radius;
};

void R_CreateShadowBuffer (QTEXTURE *buffer, int size)
{
	// size it appropriately
	size = ((size >> 1) + (SHADOWMAP_STEP_SIZE - 1)) & ~(SHADOWMAP_STEP_SIZE - 1);

	// in case it was previously allocated (e.g. we cvar-ize the shadowmap size
	buffer->Release ();

	D3D10_TEXTURE2D_DESC desc;

	desc.ArraySize = 6;
	desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.Format = DXGI_FORMAT_R16_FLOAT;
	desc.Height = size;
	desc.MipLevels = 1;
	desc.MiscFlags = D3D10_RESOURCE_MISC_TEXTURECUBE;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D10_USAGE_DEFAULT;
	desc.Width = size;

	if (SUCCEEDED (d3d_Device->CreateTexture2D (&desc, NULL, &buffer->Texture)))
	{
		// we allow these to fail silently and just disable RT lighting if they do
		d3d_Device->CreateRenderTargetView (buffer->Texture, NULL, &buffer->RTV);
		d3d_Device->CreateShaderResourceView (buffer->Texture, NULL, &buffer->SRV);

		// release the texture since we don't want anybody directly accessing it
		buffer->Texture->Release ();
		buffer->Texture = NULL;
	}

	Con_Printf (PRINT_DEVELOPER, "Created shadow buffer at %ix%i\n", size, size);
	buffer->Width = buffer->Height = size;
}


QTEXTURE d3d_DiffuseBuffer;
QTEXTURE d3d_NormalsBuffer;
QTEXTURE d3d_PositionBuffer;


void R_CreateGBuffer (QTEXTURE *buffer, int Width, int Height, DXGI_FORMAT format)
{
	// make it a multiple of 4
	Width = (Width + 3) & ~3;
	Height = (Height + 3) & ~3;

	// release the buffer in case it already existed
	buffer->Release ();

	D3D10_TEXTURE2D_DESC desc;

	desc.ArraySize = 1;
	desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.Format = format;
	desc.Height = Height;
	desc.MipLevels = 1;
	desc.MiscFlags = 0;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D10_USAGE_DEFAULT;
	desc.Width = Width;

	if (SUCCEEDED (d3d_Device->CreateTexture2D (&desc, NULL, &buffer->Texture)))
	{
		// we allow these to fail silently and just disable RT lighting if they do
		d3d_Device->CreateRenderTargetView (buffer->Texture, NULL, &buffer->RTV);
		d3d_Device->CreateShaderResourceView (buffer->Texture, NULL, &buffer->SRV);

		// release the texture since we don't want anybody directly accessing it
		buffer->Texture->Release ();
		buffer->Texture = NULL;
	}
}


ID3D10DepthStencilState *d3d_ClearStencil = NULL;
ID3D10DepthStencilState *d3d_ShadowZFail = NULL;
ID3D10DepthStencilState *d3d_ShadowZPass = NULL;
ID3D10DepthStencilState *d3d_ShadowTest = NULL;
ID3D10BlendState *d3d_BlendShadow = NULL;
ID3D10RasterizerState *d3d_RasterizerShadow = NULL;

class CRTLightHandler : public CD3DHandler
{
public:
	void OnCreateDevice (void)
	{
		// somebody should kill Bill for this...
		D3D10_DEPTH_STENCIL_DESC dssDescClear = {
			TRUE,
			D3D10_DEPTH_WRITE_MASK_ZERO,
			D3D10_COMPARISON_ALWAYS,
			TRUE,
			D3D10_DEFAULT_STENCIL_READ_MASK,
			D3D10_DEFAULT_STENCIL_WRITE_MASK,
			{D3D10_STENCIL_OP_REPLACE, D3D10_STENCIL_OP_REPLACE, D3D10_STENCIL_OP_REPLACE, D3D10_COMPARISON_ALWAYS},
			{D3D10_STENCIL_OP_REPLACE, D3D10_STENCIL_OP_REPLACE, D3D10_STENCIL_OP_REPLACE, D3D10_COMPARISON_ALWAYS}
		};

		D3D10_DEPTH_STENCIL_DESC dssDescShadowZFail = {
			TRUE,
			D3D10_DEPTH_WRITE_MASK_ZERO,
			D3D10_COMPARISON_LESS,	// shouldn't this be D3D10_COMPARISON_LESS_EQUAL???
			TRUE,
			D3D10_DEFAULT_STENCIL_READ_MASK,
			D3D10_DEFAULT_STENCIL_WRITE_MASK,
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_INCR, D3D10_STENCIL_OP_KEEP, D3D10_COMPARISON_ALWAYS},
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_DECR, D3D10_STENCIL_OP_KEEP, D3D10_COMPARISON_ALWAYS}
		};

		D3D10_DEPTH_STENCIL_DESC dssDescShadowZPass = {
			TRUE,
			D3D10_DEPTH_WRITE_MASK_ZERO,
			D3D10_COMPARISON_LESS,	// shouldn't this be D3D10_COMPARISON_LESS_EQUAL???
			TRUE,
			D3D10_DEFAULT_STENCIL_READ_MASK,
			D3D10_DEFAULT_STENCIL_WRITE_MASK,
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_DECR, D3D10_COMPARISON_ALWAYS},
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_INCR, D3D10_COMPARISON_ALWAYS}
		};

		D3D10_DEPTH_STENCIL_DESC dssDescShadowTest = {
			TRUE,
			D3D10_DEPTH_WRITE_MASK_ZERO,
			D3D10_COMPARISON_LESS,
			TRUE,
			D3D10_DEFAULT_STENCIL_READ_MASK,
			D3D10_DEFAULT_STENCIL_WRITE_MASK,
			// stencil fail			depth fail				stencil pass			comparison func
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_ZERO, D3D10_COMPARISON_EQUAL},
			{D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_KEEP, D3D10_STENCIL_OP_ZERO, D3D10_COMPARISON_EQUAL}
		};

		D3D10_BLEND_DESC bsDescShadow = {
			FALSE,
			{FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE},
			D3D10_BLEND_ONE,
			D3D10_BLEND_ZERO,
			D3D10_BLEND_OP_ADD,
			D3D10_BLEND_ONE,
			D3D10_BLEND_ZERO,
			D3D10_BLEND_OP_ADD,
			{0, 0, 0, 0, 0, 0, 0, 0}
		};

		D3D10_RASTERIZER_DESC rsDescShadow = {
			D3D10_FILL_SOLID,
			D3D10_CULL_NONE,
			FALSE,
			0,
			0,
			0,
			FALSE,	// disable depth clipping so that we don't have to use an infinite projection
			FALSE,
			FALSE,
			FALSE
		};

		d3d_Device->CreateDepthStencilState (&dssDescClear, &d3d_ClearStencil);
		d3d_Device->CreateDepthStencilState (&dssDescShadowZFail, &d3d_ShadowZFail);
		d3d_Device->CreateDepthStencilState (&dssDescShadowZPass, &d3d_ShadowZPass);
		d3d_Device->CreateDepthStencilState (&dssDescShadowTest, &d3d_ShadowTest);
		d3d_Device->CreateBlendState (&bsDescShadow, &d3d_BlendShadow);
		d3d_Device->CreateRasterizerState (&rsDescShadow, &d3d_RasterizerShadow);

		// create our constant buffers
		D3D10_BUFFER_DESC cbDesc2 = {sizeof (lightprojection_t), D3D10_USAGE_DEFAULT, D3D10_BIND_CONSTANT_BUFFER, 0, 0};
		D3D10_BUFFER_DESC cbDesc3 = {sizeof (lightparameters_t), D3D10_USAGE_DEFAULT, D3D10_BIND_CONSTANT_BUFFER, 0, 0};

		d3d_Device->CreateBuffer (&cbDesc2, NULL, &d3d_LightProjection);
		d3d_Device->CreateBuffer (&cbDesc3, NULL, &d3d_LightParameters);

		// and our vertex buffers
		D3D10_BUFFER_DESC vbDesc1 = {
			MAX_ACTIVE_LIGHTS * sizeof (lightvertex_t),
			D3D10_USAGE_DYNAMIC,
			D3D10_BIND_VERTEX_BUFFER,
			D3D10_CPU_ACCESS_WRITE,
			0
		};

		d3d_Device->CreateBuffer (&vbDesc1, NULL, &d3d_LightVertexes);

		// create our shaders
		d3d_SurfLight.CreateShaders (IDR_LIGHT, "SurfLightVS", "SurfLightGS", "CommonLightPS", INPUT_LAYOUT_SURF);
		d3d_MeshLight.CreateShaders (IDR_LIGHT, "MeshLightVS", NULL, "CommonLightPS", INPUT_LAYOUT_MESH);

		d3d_SurfShadowed.CreateShaders (IDR_LIGHT, "SurfShadowedVS", "SurfShadowedGS", "CommonShadowedPS", INPUT_LAYOUT_SURF);
		d3d_MeshShadowed.CreateShaders (IDR_LIGHT, "MeshShadowedVS", NULL, "CommonShadowedPS", INPUT_LAYOUT_MESH);

		d3d_SurfShadow.CreateShaders (IDR_LIGHT, "SurfShadowVS", "ObjectShadowGS", "ObjectShadowPS", INPUT_LAYOUT_SURF);
		d3d_MeshShadow.CreateShaders (IDR_LIGHT, "MeshShadowVS", "ObjectShadowGS", "ObjectShadowPS", INPUT_LAYOUT_MESH);

		d3d_MeshShadowVolume.CreateShaders (IDR_LIGHT, "MeshShadowVolumeVS", "ShadowVolumeGS", NULL, INPUT_LAYOUT_MESHSV);

		d3d_LightBounds.CreateShaders (IDR_LIGHT, "LightBoundsVS", NULL, NULL, INPUT_LAYOUT_LIGHT);

		d3d_DeferredQuad.CreateShaders (IDR_LIGHT, "DeferredQuadVS", NULL, "DeferredCommonPS", INPUT_LAYOUT_NONE);
		d3d_DeferredIcos.CreateShaders (IDR_LIGHT, "DeferredIcosVS", NULL, "DeferredCommonPS", INPUT_LAYOUT_XYZ);

		d3d_StencilClearQuad.CreateShaders (IDR_LIGHT, "DeferredQuadVS", NULL, NULL, INPUT_LAYOUT_NONE);
		d3d_StencilClearIcos.CreateShaders (IDR_LIGHT, "DeferredIcosVS", NULL, NULL, INPUT_LAYOUT_XYZ);

		// create a shadow buffer for dynamic lights
		// we don't know how big these are going to be so just pick a reasonable median
		R_CreateShadowBuffer (&d3d_DynamicShadowMap, 256);

		// create our light bounding volume
		d3d_LightVolume.CreateIcosahedron ();
	}

	void OnReleaseDevice (void)
	{
		R_ClearShadowBuffers ();

		d3d_DiffuseBuffer.Release ();
		d3d_NormalsBuffer.Release ();
		d3d_PositionBuffer.Release ();

		d3d_DynamicShadowMap.Release ();

		SAFE_RELEASE (d3d_BlendShadow);
		SAFE_RELEASE (d3d_ClearStencil);
		SAFE_RELEASE (d3d_ShadowZFail);
		SAFE_RELEASE (d3d_ShadowZPass);
		SAFE_RELEASE (d3d_ShadowTest);
		SAFE_RELEASE (d3d_RasterizerShadow);

		SAFE_RELEASE (d3d_LightProjection);
		SAFE_RELEASE (d3d_LightParameters);

		SAFE_RELEASE (d3d_LightVertexes);
		d3d_LightVolume.Release ();
	}

	void OnModeChange (void)
	{
		// create our GBuffers at the appropriate resolution
		R_CreateGBuffer (&d3d_DiffuseBuffer, vid.Width, vid.Height, DXGI_FORMAT_R8G8B8A8_UNORM);
		R_CreateGBuffer (&d3d_NormalsBuffer, vid.Width, vid.Height, DXGI_FORMAT_R8G8B8A8_SNORM);
		R_CreateGBuffer (&d3d_PositionBuffer, vid.Width, vid.Height, DXGI_FORMAT_R32G32B32A32_FLOAT);

		ID3D10Buffer *cBuffers[] = {d3d_LightProjection, d3d_LightParameters};

		d3d_Device->VSSetConstantBuffers (5, 2, cBuffers);
		d3d_Device->GSSetConstantBuffers (5, 2, cBuffers);
		d3d_Device->PSSetConstantBuffers (5, 2, cBuffers);
	}
} d3d_RTLightHandler;


QTEXTURE *R_GetShadowBuffer (dlight_t *dl)
{
	return NULL;
}


void R_ClearShadowBuffer (int num)
{
}


void R_ClearShadowBuffers (void)
{
}


void R_SetupLightParameters (dlight_t *dl, float *OriginToUse)
{
	// half the scale again because the style values are boosted for overbrights
	float lightscale = r_dynamic->value * dl->stylevalue * dl->diffusescale * 0.5f;

	// make 6 matrixes for projecting to our cubemap
	lightparameters_t c;

	// add the light position and radius to it
	Vector3Copy (c.lightposition, dl->origin);
	c.lightradius = dl->radius;
	Vector3Scale (c.lightcolour, lightscale, dl->color);
	c.zpass = dl->Inside ? -1.0f : 1.0f;	// so that we can do reasonable comparison in shader code
	c.falloff = dl->falloff;
	Vector3Copy (c.spotvec, dl->spotvec);

	// and update our cbuffer
	d3d_Device->UpdateSubresource (d3d_LightParameters, 0, NULL, &c, 0, 0);
}


void R_SetupLightProjection (dlight_t *dl)
{
	lightprojection_t c;

	QMATRIX ShadowProjection;

	ShadowProjection.Identity ();
	ShadowProjection.Perspective (90, 90, 4.0f, 14188.960f);

	for (int face = 0; face < 6; face++)
	{
		QMATRIX ShadowView;
		QMATRIX ShadowMVP;

		// view is updated every frame
		ShadowView.Identity ();
		ShadowView.AlignCubemapFace (face, dl->origin);

		// the world matrix will be taken from the entity's local matrix so don't bother with one here
		ShadowMVP.Mult (ShadowView, ShadowProjection);

		// and update it to our cbuffer data
		ShadowMVP.Get (c.matrix[face]);
	}

	// update depthbias and ztrick
	c.depthbias = r_shadowmapdepthbias->value;
	c.trickframe = (d3d_ShadowTrickFrame & 1) ? -1 : 1;

	// and update our cbuffer
	d3d_Device->UpdateSubresource (d3d_LightProjection, 0, NULL, &c, 0, 0);
}


int R_GetLightDist (msurface_t *surf, float *impact, int vec)
{
	float l = Vector3Dot (impact, surf->texinfo->vecs[vec]) + surf->texinfo->vecs[vec][3] - surf->texturemins[vec];
	int d = l + 0.5f;
	
	if (d < 0)
		return l;
	else if (d > surf->extents[vec])
		return l - surf->extents[vec];
	else return l - d;
}


void dlight_t::MarkLights (model_t *m, mnode_t *node)
{
start:;
	// end of the line
	if (node->contents < 0) return;

	// don't mark nodes that are not visible
	if (node->visframe != ref.VisFrameCount) return;

	// find out which side of the node we're on
	mplane_t *splitplane = node->plane;
	float dist = Vector3DotH (this->transformed, splitplane->normal, -splitplane->dist);

	if (dist > this->radius) {node = node->children[0]; goto start;}
	if (dist < -this->radius) {node = node->children[1]; goto start;}

	// mark the polygons
	msurface_t *surf = m->surfaces + node->firstsurface;

	// the surface plane is the same as the node plane so only calculate this once
	// (that's the definition of "surfaces on a node" after all)
	float impact[3] = {
		this->transformed[0] - splitplane->normal[0] * dist,
		this->transformed[1] - splitplane->normal[1] * dist,
		this->transformed[2] - splitplane->normal[2] * dist
	};

	// and now go through all the surfaces
	for (int i = 0; i < node->numsurfaces; i++, surf++)
	{
		// don't bother marking sky or water surfaces
		if (surf->flags & SURF_DRAWSKY) continue;
		if (surf->flags & SURF_DRAWTURB) continue;

		// only mark for surfaces in the PVS
		if (surf->visframe != ref.FrameCount) continue;

		// don't mark backfacing surfaces (but only for shadow-casting lights)
		if (this->Shadow && ((dist < 0) ^ !!(surf->flags & SURF_PLANEBACK))) continue;

		// clamp center of light to corner and check brightness
		int s = R_GetLightDist (surf, impact, 0);
		int t = R_GetLightDist (surf, impact, 1);

		// compare to minimum light
		if ((s * s + t * t + dist * dist) < this->maxdist)
		{
			// add the surface to the chain for this light
			R_AddSurfaceToTextureChain (surf, CHAIN_DYNAMICLIGHT);
			this->Marked = TRUE;
		}
	}

	// recurse down both sides
	this->MarkLights (m, node->children[0]);
	this->MarkLights (m, node->children[1]);
}


void dlight_t::Draw (entity_t *ent)
{
	if (!ent) return;

	// signal that we're going to a new set of passes so that state will update properly
	ref.AliasPasses++;
	ref.BrushPasses++;

	model_t *m = ent->model;

	// precompute maxdist for marklights/etc removal
	this->maxdist = this->radius * this->radius;

	// transform the light back to the entity's frame of reference
	ent->localmatrix.InverseTransform (this->transformed, this->origin);

	// model type specific passes
	if (m->type == mod_alias)
	{
		// check if the dlight sphere hits the model's bbox
		if (!Mod_SphereBoxCollide (ent->bbox.mins, ent->bbox.maxs, ent->bbox.mids, this->origin, this->radius)) return;

		// draw with the appropriate shaders
		if (r_realtime_lighting->boolean && this->Shadow)
			R_BruteForceAliasModel (ent, &d3d_MeshShadowed, d3d_BlendAdditive, d3d_ZmodeNowrite);
		else
		{
			R_SetupLightParameters (this, this->transformed);
			R_BruteForceAliasModel (ent, &d3d_MeshLight, d3d_BlendAdditive, d3d_ZmodeNowrite);
		}
	}
	else if (m->type == mod_brush)
	{
		// nothing uses this light by default
		this->Marked = FALSE;

		// mark the surfaces for this light (this is valid for the worldmodel and all bmodels)
		this->MarkLights (m, m->nodes + m->hulls[0].firstclipnode);

		// nothing was added
		if (!this->Marked) return;

		// draw with the appropriate shaders
		if (r_realtime_lighting->boolean && this->Shadow)
			this->DrawBrush (ent, m, &d3d_SurfShadowed);
		else
		{
			R_SetupLightParameters (this, this->transformed);
			this->DrawBrush (ent, m, &d3d_SurfLight);
		}
	}
}


void R_DrawDynamicLights (entity_t *e)
{
	// draw all of our active lights
	for (int i = 0; i < r_numactivelights; i++)
	{
		dlight_t *dl = r_activelights[i];
		dl->Draw (e);
	}
}


#if 1
void R_RecursiveShadowNode (dlight_t *dl, model_t *m, mnode_t *node, texturechain_t *chain)
{
start:;
	// end of the line
	if (node->contents < 0) return;

	// find out which side of the node we're on
	mplane_t *splitplane = node->plane;
	float dist = Vector3DotH (dl->transformed, splitplane->normal, -splitplane->dist);

	if (dist > dl->radius) {node = node->children[0]; goto start;}
	if (dist < -dl->radius) {node = node->children[1]; goto start;}

	msurface_t *surf = m->surfaces + node->firstsurface;

	for (int c = 0; c < node->numsurfaces; c++, surf++)
	{
		// these aren't light blockers (note - sky shouldn't block light either - see e1m5)
		if (surf->flags & SURF_DRAWSKY) continue;
		if (surf->flags & SURF_DRAWTURB) continue;
		if ((dist < 0) ^ !!(surf->flags & SURF_PLANEBACK)) continue;

		// add the surface to it's ZFill chain
		R_AddSurfaceToTextureChain (surf, chain, CHAIN_ZFILL);
	}

	// recurse down both sides
	R_RecursiveShadowNode (dl, m, node->children[0], chain);
	R_RecursiveShadowNode (dl, m, node->children[1], chain);
}
#else
void R_RecursiveShadowNode (dlight_t *dl, model_t *m, mnode_t *node, texturechain_t *chain)
{
	// node is solid or a leaf
	if (node->contents < 0) return;

	// find which side of the node we are on
	mplane_t *plane = node->plane;
	float dot = Vector3Dot (dl->transformed, plane->normal) - plane->dist;
	int side = (dot >= 0) ? 0 : 1;

	// stay on target
	if (dot > dl->radius && side == 1) return;
	if (dot < -dl->radius && side == 0) return;

	// recurse down the children, front side first
	R_RecursiveShadowNode (dl, m, node->children[side], chain);

	if (!(dot > dl->radius || dot < -dl->radius))
	{
		// draw stuff
		msurface_t *surf = m->surfaces + node->firstsurface;

		for (int c = 0; c < node->numsurfaces; c++, surf++)
		{
			// these aren't light blockers (note - sky shouldn't block light either - see e1m5)
			if (surf->flags & SURF_DRAWSKY) continue;
			if (surf->flags & SURF_DRAWTURB) continue;
			if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) continue;

			// add the surface to it's ZFill chain
			R_AddSurfaceToTextureChain (surf, chain, CHAIN_ZFILL);
		}
	}

	// stay on target
	if (dot > dl->radius && side == 0) return;
	if (dot < -dl->radius && side == 1) return;

	// recurse down the back side
	R_RecursiveShadowNode (dl, m, node->children[!side], chain);
}
#endif


void R_DrawShadowBufferBrush (dlight_t *dl, entity_t *e)
{
	/*
	// signify that we're going to a new brush pass
	ref.BrushPasses++;

	texturechain_t ShadowChain;
	model_t *clmodel = e->model;

	// transform the light back to the entity's frame of reference
	e->localmatrix.InverseTransform (dl->transformed, dl->origin);

	// build a new chain of shadowed surfaces
	R_ClearTextureChain (&ShadowChain);
	R_RecursiveShadowNode (dl, clmodel, clmodel->nodes + clmodel->hulls[0].firstclipnode, &ShadowChain);

	if (ShadowChain.TextureChain)
	{
		// set up the basic draw state for surfaces
		R_SetupSurfaceDrawState (e, clmodel);

		// bind our shaders
		d3d_SurfShadow.Bind ();

		// using a minimum blend to simulate depth testing here enables us to draw without a depth buffer
		d3d_State->OMSetBlendState (d3d_BlendMin);
		d3d_State->OMSetDepthState (d3d_ZmodeNone);

		// and draw the chain
		R_DrawNewTextureChain (&ShadowChain, CHAIN_ZFILL);
	}
	*/
}


void R_DrawShadowBufferAlias (dlight_t *dl, entity_t *e)
{
	if (Mod_SphereBoxCollide (e->bbox.mins, e->bbox.maxs, e->bbox.mids, dl->origin, dl->radius))
	{
		// signal that we're going to a new pass so state will refresh
		ref.AliasPasses++;

		// and draw it
		R_BruteForceAliasModel (e, &d3d_MeshShadow, d3d_BlendMin, d3d_ZmodeNone);
	}
}


void R_PredicateLights (void)
{
}


void R_UpdateShadowMap (dlight_t *dl, QTEXTURE *ShadowBuffer, entity_t **entities, int numentities)
{
}


void R_DrawRealtimeScene666 (entity_t **entities, int numentities)
{
}


void R_SwitchToGBuffer (void)
{
	// nothing to draw
	if (!r_realtime_lighting->boolean) return;

	// check
	if (!d3d_DiffuseBuffer.RTV || !d3d_DiffuseBuffer.SRV) return;
	if (!d3d_NormalsBuffer.RTV || !d3d_NormalsBuffer.SRV) return;
	if (!d3d_PositionBuffer.RTV || !d3d_PositionBuffer.SRV) return;

	// switch to our 4 render targets
	ID3D10RenderTargetView *RTVs[] = {d3d_DiffuseBuffer.RTV, d3d_NormalsBuffer.RTV, d3d_PositionBuffer.RTV};
	d3d_Device->OMSetRenderTargets (3, RTVs, d3d_DepthBuffer.DSV);
}

void R_DrawShadows (dlight_t *dl, entity_t **entities, int numentities);

void R_DrawShadows666 (dlight_t *dl, entity_t **entities, int numentities)
{
	// go to the next trick frame
	d3d_ShadowTrickFrame++;

	// unbind the shadow map from input
	ID3D10ShaderResourceView *NullSRV = NULL;
	d3d_Device->PSSetShaderResources (18, 1, &NullSRV);

	// switch render targets
	d3d_Device->OMSetRenderTargets (1, &d3d_DynamicShadowMap.RTV, NULL);

	// select the appropriate viewport
	D3D10_VIEWPORT vp1 = {0, 0, d3d_DynamicShadowMap.Width, d3d_DynamicShadowMap.Height, 0, 1};
	d3d_Device->RSSetViewports (1, &vp1);

	// because we're using a minimum blend and writing distance to the output, we just need to
	// clear to the light radius; anything beyond that is not hit by the light by default
	float clearcolor[4] = {dl->radius, dl->radius, dl->radius, dl->radius};
	d3d_Device->ClearRenderTargetView (d3d_DynamicShadowMap.RTV, clearcolor);

	// set up the light projection
	R_SetupLightProjection (dl);

	// shadow map everything
	R_DrawShadowBufferBrush (dl, &r_worldentity);

	for (int i = 0; i < numentities; i++)
	{
		entity_t *e = entities[i];
		model_t *clmodel = e->model;

		// these entities are not light blockers
		if (e->effects & EF_STATICENT) continue;
		if (e == &cl.viewent) continue;

		// entities shouldn't block their own light but may block others
		if (dl->key == e->entitynum) continue;

		// lights inside an entity shouldn't be blocked by it
		if (dl->origin[0] > e->bbox.mins[0] && dl->origin[0] < e->bbox.maxs[0] &&
			dl->origin[1] > e->bbox.mins[1] && dl->origin[1] < e->bbox.maxs[1] &&
			dl->origin[2] > e->bbox.mins[2] && dl->origin[2] < e->bbox.maxs[2])
			continue;

		// and draw it
		if (clmodel->type == mod_brush)
			R_DrawShadowBufferBrush (dl, e);
		else R_DrawShadowBufferAlias (dl, e);
	}

	// put the old render target back (this will unbind the shadow map from output automatically)
	d3d_Device->OMSetRenderTargets (1, &ref.RenderTarget->RTV, NULL);
	// (AMD likes to touch the depth buffer even if depth test is fully disabled so we disable that too)

	// bind the shadow map for input
	d3d_Device->PSSetShaderResources (18, 1, &d3d_DynamicShadowMap.SRV);

	// put the viewport back
	D3D10_VIEWPORT vp2 = {0, 0, vid.ViewWidth, vid.ViewHeight, 0, 1};
	d3d_Device->RSSetViewports (1, &vp2);
}


void R_DrawDeferredLight (dlight_t *dl, QSHADER *InsideShader, QSHADER *OutsideShader, ID3D10BlendState *bs, ID3D10DepthStencilState *ds)
{
	d3d_State->OMSetBlendState (bs);
	d3d_State->OMSetDepthState (ds);

	if (dl->Inside)
	{
		// if the view is inside the light draw it as a fullscreen quad
		InsideShader->Bind ();
		d3d_Device->IASetPrimitiveTopology (D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
		d3d_Device->Draw (4, 0);
		c_draw_call++;
	}
	else
	{
		// if the view is outside the light draw it as a light volume
		OutsideShader->Bind ();
		d3d_Device->IASetIndexBuffer (d3d_LightVolume.IndexBuffer, DXGI_FORMAT_R16_UINT, 0);
		d3d_Device->IASetPrimitiveTopology (D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		d3d_Device->DrawIndexed (d3d_LightVolume.NumIndexes, 0, 0);
		c_draw_call++;
	}
}


void R_DrawRealtimeScene (entity_t **entities, int numentities)
{
	// nothing to draw
	if (!r_realtime_lighting->boolean) return;

	// put the old render targets back (this will unbind the gbuffers from output)
	//d3d_Device->OMSetRenderTargets (1, &ref.RenderTarget->RTV, NULL);
	d3d_Device->OMSetRenderTargets (1, &ref.RenderTarget->RTV, d3d_DepthBuffer.DSV);
	// (AMD likes to touch the depth buffer even if depth test is fully disabled so we disable that too)

	// nothing to draw
	if (!r_numactivelights) return;

	// bind the gbuffers for input
	ID3D10ShaderResourceView *GBufferSRVs[] = {d3d_DiffuseBuffer.SRV, d3d_NormalsBuffer.SRV, d3d_PositionBuffer.SRV};
	d3d_Device->PSSetShaderResources (TEXTURE_SLOT_GBUFFER1, 3, GBufferSRVs);

	// set a viewport covering the screen region we're interested in
	D3D10_VIEWPORT vp = {0, 0, vid.ViewWidth, vid.ViewHeight, 0, 1};
	d3d_Device->RSSetViewports (1, &vp);

	// set up our vertex buffers; because slot 8 is used for generic shapes we need to do this every time
	UINT strides[] = {sizeof (lightvertex_t), sizeof (float) * 3};
	UINT offsets[] = {0, 0};
	ID3D10Buffer *Buffers[] = {d3d_LightVertexes, d3d_LightVolume.VertexBuffer};

	d3d_Device->IASetVertexBuffers (BUFFER_SLOT_LIGHTS, 2, Buffers, strides, offsets);

	// per-light
	for (int lnum = 0; lnum < r_numactivelights; lnum++)
	{
		dlight_t *dl = r_activelights[lnum];

		// check for a viewpoint inside the light volume
		dl->Inside = (Vector3Length (dl->origin, ref.ViewOrigin) < dl->radius * 1.33f);

		// setup to light the scene
		R_SetupLightParameters (dl, dl->origin);

		// draw shadows
		R_DrawShadows (dl, entities, numentities);

		// draw the light where the stencil buffer was unmarked (it's still faster to use light volumes than
		// it is to rely on the stencil buffer alone for handling regions outside of the light's area)
		R_DrawDeferredLight (dl, &d3d_DeferredQuad, &d3d_DeferredIcos, d3d_BlendAdditive, d3d_ShadowTest);//d3d_ZmodeNone);
	}

	// put the old render targets back (including depth) - this unbinds the light buffer from output
	//d3d_Device->OMSetRenderTargets (1, &ref.RenderTarget->RTV, d3d_DepthBuffer.DSV);

	// unbind the GBuffers from input
	ID3D10ShaderResourceView *NullSRVs[] = {NULL, NULL, NULL, NULL};
	d3d_Device->PSSetShaderResources (TEXTURE_SLOT_GBUFFER1, 4, NullSRVs);

	// pickup fullbrights on MDLs
	for (int i = 0; i < numentities; i++)
	{
		entity_t *e = entities[i];
		model_t *clmodel = e->model;

		// the entity is on the list but has been culled; we don't want to draw the entity but we do want to
		// draw it's shadow
		if (e->culled) continue;

		if (clmodel->type == mod_alias)
		{
			ref.AliasPasses++;
			R_DrawAliasLuma (e);
		}
	}
}


void R_DrawShadows (dlight_t *dl, entity_t **entities, int numentities)
{
	// because we can have a different shadow volume for each surface in the world, and for each entity, the state change
	// overhead would be crippling, so we're just going to use depth-fail everywhere; z-buffer optimizations are not so
	// important at this stage because we're using a deferred renderer and therefore most of our z-buffer work for the
	// frame has already been done.  also set the rest of our states here too
	d3d_Device->RSSetState (d3d_RasterizerShadow);
	d3d_State->OMSetBlendState (d3d_BlendShadow);

	if (dl->Inside)
	{
		d3d_Device->ClearDepthStencilView (d3d_DepthBuffer.DSV, D3D10_CLEAR_STENCIL, 1, 0);
		d3d_State->OMSetDepthState (d3d_ShadowZFail);
	}
	else
	{
		R_DrawDeferredLight (dl, &d3d_StencilClearQuad, &d3d_StencilClearIcos, d3d_BlendZFill, d3d_ClearStencil);
		d3d_State->OMSetDepthState (d3d_ShadowZPass);
	}

	// do the world
	// even though shadow volumes require closed meshes, the world can be treated as if it were a closed mesh because the
	// point at which it goes off the PVS will always be a silhouette edge anyway, so no special handling is needed.
	// (alternatively see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch11.html for a non-closed mesh)

	// the more i look at this, the more it seems that shadow volumes are an absolutely perfect fit for a deferred renderer

	for (int i = 0; i < numentities; i++)
	{
		entity_t *e = entities[i];
		model_t *clmodel = e->model;

		// these entities are not light blockers
		if (e->effects & EF_STATICENT) continue;
		if (e == &cl.viewent) continue;

		// entities shouldn't block their own light but may block others
		if (dl->key == e->entitynum) continue;

		// lights inside an entity shouldn't be blocked by it
		if (dl->origin[0] > e->bbox.mins[0] && dl->origin[0] < e->bbox.maxs[0] &&
			dl->origin[1] > e->bbox.mins[1] && dl->origin[1] < e->bbox.maxs[1] &&
			dl->origin[2] > e->bbox.mins[2] && dl->origin[2] < e->bbox.maxs[2])
			continue;

		// and draw it
		// all entity passes must be transformed the same way otherwise fp imprecision may cause z-fighting
		// everywhere there is "vs_out.Position = mul (EntityMatrix, Position);" replace it with:
		// vs_out.Position = mul (EntityMatrix, mul (LocalMatrix, Position)); - EntityMatrix then takes
		// either a copy of the mvp matrix or a scaled copy of it if it's a depth hack entity; this effect
		// is already visible on torches & other fullbrights
		if (clmodel->type == mod_brush)
		{
			ref.BrushPasses++;
		}
		else
		{
			if (Mod_SphereBoxCollide (e->bbox.mins, e->bbox.maxs, e->bbox.mids, dl->origin, dl->radius))
			{
				ref.AliasPasses++;
				d3d_MeshShadowVolume.Bind ();
				R_DrawAliasAdjacency (e);
			}
		}
	}

	// put the rasterizer back; the others will go back themselves at the next drawing batch
	d3d_Device->RSSetState (d3d_RasterizerDefault);
}


